nexus\api\rtapi/
group.rs

1use super::RealTimeData;
2use bitfields::bitfield;
3use num_enum::{IntoPrimitive, TryFromPrimitive};
4use std::ffi::{CStr, c_char};
5
6#[derive(Debug, Copy, Clone)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct GroupData {
9    /// Locations of squad markers in the game world as ingame coordinates.
10    pub squad_markers: [[f32; 3]; 8],
11
12    /// Type of current group.
13    pub group_type: Result<GroupType, u32>,
14
15    /// Number of members in current group.
16    pub group_member_count: u32,
17}
18
19impl GroupData {
20    /// Reads group data from the given data pointer.
21    ///
22    /// # Safety
23    /// The pointer must be safe to read from.
24    pub unsafe fn read(data: *const RealTimeData) -> Self {
25        unsafe {
26            Self {
27                squad_markers: (*data).squad_markers,
28                group_type: (*data).group_type.try_into(),
29                group_member_count: (*data).group_member_count,
30            }
31        }
32    }
33}
34
35#[derive(
36    Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
37)]
38#[num_enum(error_type(name = u32, constructor = From::from))]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40#[cfg_attr(
41    feature = "strum",
42    derive(
43        strum::AsRefStr,
44        strum::Display,
45        strum::EnumCount,
46        strum::EnumIter,
47        strum::IntoStaticStr,
48        strum::VariantArray,
49        strum::VariantNames
50    )
51)]
52#[repr(u32)]
53pub enum GroupType {
54    None,
55    Party,
56    RaidSquad,
57    Squad,
58}
59
60/// Group member.
61///
62/// This struct uses the C layout.
63/// Instead of cloning this it is recommended to convert to [`GroupMemberOwned`] via [`Into`] or [`to_owned`](GroupMember::to_owned).
64#[derive(Debug, Clone)]
65#[repr(C)]
66pub struct GroupMember {
67    /// Account name of the group member.
68    account_name: [c_char; 140],
69
70    /// Character name of the group member.
71    character_name: [c_char; 140],
72
73    /// Subgroup of the group member.
74    ///
75    /// 0 when in a party.
76    /// 1-15 subgrups when in a squad.
77    pub subgroup: u32,
78
79    /// Profession of the group member.
80    ///
81    /// 0 when unknown, for example on loading screen or character select.
82    pub profession: u32,
83
84    /// 3rd specialization of the group member (not always elite).
85    ///
86    /// 0 when unknown, for example on loading screen or character select.
87    pub elite_specialization: u32,
88
89    /// Member flags.
90    flags: GroupMemberFlags,
91}
92
93impl GroupMember {
94    /// Converts the member to a [`GroupMemberOwned`].
95    #[inline]
96    pub fn to_owned(&self) -> GroupMemberOwned {
97        self.into()
98    }
99
100    /// Returns the account name of the member as pointer.
101    #[inline]
102    pub const fn account_name_ptr(&self) -> *const c_char {
103        self.account_name.as_ptr()
104    }
105
106    /// Returns the account name of the member as [`CStr`].
107    #[inline]
108    pub fn account_name_cstr(&self) -> &CStr {
109        unsafe { CStr::from_ptr(self.account_name.as_ptr()) }
110    }
111
112    /// Returns the account name of the member as owned [`String`].
113    #[inline]
114    pub fn account_name(&self) -> String {
115        self.account_name_cstr().to_string_lossy().into_owned()
116    }
117
118    /// Returns the character name of the member as pointer.
119    #[inline]
120    pub const fn character_name_ptr(&self) -> *const c_char {
121        self.character_name.as_ptr()
122    }
123
124    /// Returns the account name of the member as [`CStr`].
125    #[inline]
126    pub fn character_name_cstr(&self) -> &CStr {
127        unsafe { CStr::from_ptr(self.character_name.as_ptr()) }
128    }
129
130    /// Returns the character name of the member as owned [`String`].
131    #[inline]
132    pub fn character_name(&self) -> String {
133        self.character_name_cstr().to_string_lossy().into_owned()
134    }
135
136    /// Returns the flags of the member.
137    #[inline]
138    pub const fn flags(&self) -> GroupMemberFlags {
139        self.flags
140    }
141
142    /// Returns whether the member is self (the local player).
143    #[inline]
144    pub const fn is_self(&self) -> bool {
145        self.flags.is_self()
146    }
147
148    /// Returns whether the member is in the current instance.
149    #[inline]
150    pub const fn is_in_instance(&self) -> bool {
151        self.flags.is_in_instance()
152    }
153
154    /// Returns whether the member if the commander of the current squad.
155    #[inline]
156    pub const fn is_commander(&self) -> bool {
157        self.flags.is_commander()
158    }
159
160    /// Returns whether the member is a lieutenant in the current squad.
161    #[inline]
162    pub const fn is_lieutenant(&self) -> bool {
163        self.flags.is_lieutenant()
164    }
165}
166
167#[bitfield(u32)]
168#[derive(Copy, Clone, PartialEq, Eq)]
169#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170pub struct GroupMemberFlags {
171    pub is_self: bool,
172    pub is_in_instance: bool,
173    pub is_commander: bool,
174    pub is_lieutenant: bool,
175
176    #[bits(28)]
177    _padding: u32,
178}
179
180/// Group Member as owned version.
181#[derive(Debug, Clone)]
182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
183pub struct GroupMemberOwned {
184    /// Account name of the group member.
185    pub account_name: String,
186
187    /// Character name of the group member.
188    pub character_name: String,
189
190    /// 0 for parties, 1-15 according to the squad's subgroup
191    pub subgroup: u32,
192
193    /// 0-9 = Profession; -1 Unknown -> e.g. on loading screen or logged out
194    pub profession: u32,
195
196    /// Third Spec ID, not necessarily elite; or -1 Unknown -> e.g. on loading screen or logged out
197    pub elite_specialization: u32,
198
199    /// Is this member the player themselves?
200    pub is_self: bool,
201
202    /// Is in the same map instance as the player.
203    pub is_in_instance: bool,
204
205    /// Is this member a commander?
206    pub is_commander: bool,
207
208    /// Is this member a lieutenant?
209    pub is_lieutenant: bool,
210}
211
212impl From<&GroupMember> for GroupMemberOwned {
213    fn from(member: &GroupMember) -> Self {
214        Self {
215            account_name: member.account_name(),
216            character_name: member.character_name(),
217            subgroup: member.subgroup,
218            profession: member.profession,
219            elite_specialization: member.elite_specialization,
220            is_self: member.is_self(),
221            is_in_instance: member.is_in_instance(),
222            is_commander: member.is_commander(),
223            is_lieutenant: member.is_lieutenant(),
224        }
225    }
226}